package com.diorsunion.hedge.web.interceptor;

import com.diorsunion.hedge.web.controller.JsonResult;
import com.diorsunion.hedge.util.LogUtil;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;

/**
 * Created by dingshanyyj on 15/12/24.
 */
public class ExceptionInterceptor extends ExceptionHandlerExceptionResolver {
    private String defaultErrorView = "error";
    private HttpMessageConverter jsonConverter;

    public void setJsonConverter(HttpMessageConverter jsonConverter) {
        this.jsonConverter = jsonConverter;
    }

    public ModelAndView doResolveHandlerMethodException(
            HttpServletRequest request,
            HttpServletResponse response,
            HandlerMethod handlerMethod,
            Exception exception) {

        if (handlerMethod == null) {
            return null;
        }

        Method method = handlerMethod.getMethod();

        if (method == null) {
            return null;
        }

        ModelAndView returnValue = super.doResolveHandlerMethodException(request, response, handlerMethod, exception);

        ResponseBody responseBodyAnn = AnnotationUtils.findAnnotation(method, ResponseBody.class);
        if (responseBodyAnn != null) {
            try {
                ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(method, ResponseStatus.class);
                if (responseStatusAnn != null) {
                    HttpStatus responseStatus = responseStatusAnn.value();
                    String reason = responseStatusAnn.reason();
                    if (!StringUtils.hasText(reason)) {
                        response.setStatus(responseStatus.value());
                    } else {
                        try {
                            response.sendError(responseStatus.value(), reason);
                        } catch (IOException e) {
                        }
                    }
                }

                return handleResponseBody(returnValue, request, response, handlerMethod, exception);
            } catch (Exception e) {
                return null;
            }
        }

        if (returnValue.getViewName() == null) {
            returnValue.setViewName(defaultErrorView);
        }

        return returnValue;
    }

    private ModelAndView handleResponseBody(
            ModelAndView returnValue,
            HttpServletRequest request,
            HttpServletResponse response,
            HandlerMethod handlerMethod,
            Exception exception) throws ServletException, IOException {


        final JsonResult jsonResult = JsonResult.errorResult(exception.getMessage());
        HttpInputMessage inputMessage = new ServletServerHttpRequest(request);
        List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();
        if (acceptedMediaTypes.isEmpty()) {
            acceptedMediaTypes = Collections.singletonList(MediaType.ALL);
        }
        MediaType.sortByQualityValue(acceptedMediaTypes);
        HttpOutputMessage outputMessage = new ServletServerHttpResponse(response);
        acceptedMediaTypes.stream()
                .filter(x -> jsonConverter.canWrite(JsonResult.class, x))
                .forEach(x -> {
                    try {
                        jsonConverter.write(jsonResult, x, outputMessage);
                    } catch (Throwable e) {
                        LogUtil.error("output error:" + jsonResult, e);
                    }
                });
        return new ModelAndView();
    }

}
